//! 宏 Macro
//!宏定义格式是： macro_rules! macro_name { macro_body }，其中 macro_body 与模式匹配很像， pattern => do_something ，
//! 所以 Rust 的宏又称为 Macro by example (基于例子的宏)。其中 pattern 和 do_something
//! 都是用配对的括号括起来的，括号可以是圆括号、方括号、花括号中的任意一种。匹配可以有多个分支，每个分支以分号结束。

///这个简单的例子是用来创建函数，生成的函数可以像普通函数一样调用，这个函数可以打印自己的名字。编译器在看到
/// create_function!(foo) 时会从前面去找一个叫 create_function 的宏定义，找到之后，就会尝试将
/// 参数 foo 代入 macro_body，对每一条模式按顺序进行匹配，只要有一个匹配上，就会将 => 左边定义的参数代入右边进行替换，
/// 如果替换不成功，编译器就会报错而不会往下继续匹配，替换成功就会将右边替换后的代码放在宏调用的地方。
/// 这个例子中只有一个模式，即 $func_name:ident，表示匹配一个标识符，如果匹配上就把这个标识符赋值给 $func_name，
/// 宏定义里面的变量都是以 $ 开头的，相应的类型也是以冒号分隔说明，这里 ident 是变量 $func_name 的类型，
/// 表示这个变量是一个 identifier，这是语法层面的类型(designator)，而普通的类型如 char, &str, i32, f64
/// 这些是语义层面的类型。在 main 函数中传给宏调用 create_function 的参数 foo 正好是一个标识符(ident)，
/// 所以能匹配上，$func_name 就等于 foo，然后把 $func_name 的值代入 => 右边，成了下面这样的
/// fn foo() {
///     println!("function {:?} is called", stringify!(foo))
/// }
macro_rules! create_function {
    ($func_name:ident) => (
        fn $func_name() {
            println!("function {:?} is called", stringify!($func_name))
        }
    )
}

#[test]
pub fn macro_demo1(){
    create_function!(foo);
    //宏展开得到:
    // fn foo() {
    //         println!("function {:?} is called", stringify!(foo))
    //     }
    foo();
}

macro_rules! my_print {
    ($x:ident) => {
        println!("my:{}", $x);
    }
}

#[test]
pub fn macro_demo2(){
    let a = 34;
    my_print!(a);
    //宏展开得到:
    //println!("my:{}", a);
}
//指示符(designator),宏里面的变量都是以 $ 开头的，
// 其余的都是按字面去匹配，以 $ 开头的变量都是用来表示语法(syntactic)元素，为了限定匹配什么类型的语法元素，
// 需要用指示符(designator)加以限定，就跟普通的变量绑定一样用冒号将变量和类型分开，当前宏支持以下几种指示符：
//ident: 标识符，用来表示函数或变量名
// expr: 表达式
// block: 代码块，用花括号包起来的多个语句
// pat: 模式，普通模式匹配（非宏本身的模式）中的模式，例如 Some(t), (3, 'a', _)
// path: 路径，注意这里不是操作系统中的文件路径，而是用双冒号分隔的限定名(qualified name)，如 std::cmp::PartialOrd
// tt: 单个语法树
// ty: 类型，语义层面的类型，如 i32, char
// item: 条目，
// meta: 元条目
// stmt: 单条语句，如 let a = 42;

//重复(repetition),
//宏相比函数一个很大的不同是宏可以接受任意多个参数，例如 println! 和 vec!。这是怎么做到的呢？
//没错，就是重复(repetition)。模式的重复不是通过程序里面的循环(for/while)去控制的，而是指定了两个特殊符号 + 和 *，
// 类似于正则表达式，因为正则表达式也是不关心具体匹配对象是一个人名还是一个国家名。与正则表达式一样， + 表示一次或多次（至少一次），
// 而 * 表示零次或多次。重复的模式需要用括号括起来，外面再加上 $，例如 $(...)*, $(...)+。需要说明的是这里的括号和宏里
// 面其它地方一样都可以是三种括号中的任意一种，因为括号在这里仅仅是用来标记一个模式的开始和结束，大部分情况重复的模式是
// 用逗号或分号分隔的，所以你会经常看到 $(...),*, $(...);*, $(...),+, $(...);+ 这样的用来表示重复。

//还是来看一个例子
macro_rules! vector {
//首先看 => 左边，最外层是圆括号，前面说过这个括号可以是圆括号、方括号、花括号中的任意一种，只要是配对的就行。
// 然后再看括号里面 $(...),* 正是刚才提到的重复模式，重复的模式是用逗号分隔的，重复的内容是 $x:expr，即可以
// 匹配零次或多次用逗号分隔的表达式，例如 vector![] 和 vector![3, x*x, s-t] 都可以匹配成功
    ($($x:expr),*) => {
        {
            let mut temp_vec = Vec::new();
            $(temp_vec.push($x);)*
            temp_vec
        }
    };
}

#[test]
pub fn macro_demo3(){
    let a = vector![1, 2, 3, 4];
    println!("{:?}", a);
    //宏展开得到:
    let a = {
        let mut temp_vec = Vec::new();
        temp_vec.push(1);
        temp_vec.push(2);
        temp_vec.push(3);
        temp_vec.push(4);
        temp_vec
    };
    println!("{:?}", a);
}